package us.sunrisemorning.bilibili_live_chat.ws;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import us.sunrisemorning.bilibili_live_chat.BiliBiliLiveChatMessageHandler;
import us.sunrisemorning.bilibili_live_chat.Constants;

import java.net.URI;
import java.nio.ByteBuffer;

@Slf4j
public class BiliBiliWebSocketClient extends WebSocketClient {

    private int roomId;
    private String token;
    private BiliBiliLiveChatMessageHandler messageHandler;

    private Thread heartbeat;
    private boolean running;

    public BiliBiliWebSocketClient(String url, int roomId, String token, BiliBiliLiveChatMessageHandler messageHandler) {
        super(URI.create(url));

        this.roomId = roomId;
        this.token = token;
        this.messageHandler = messageHandler;

        heartbeat = new Thread(() -> {
            WebSocketMessage heartbeat = new WebSocketMessage("[object Object]", Constants.WS_OP_HEARTBEAT);
            byte[] data = heartbeat.toByteArrayMessage();
            while (running) {
                this.send(data);
                try {
                    Thread.sleep(Constants.HEARTBEAT_SLEEP_TIME);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    @Override
    public void onOpen(ServerHandshake handshakedata) {
        log.debug("onOpen");
        String authMessage = "{\"uid\":0,\"roomid\":" + roomId + ",\"protover\":2,\"platform\":\"web\",\"type\":2,\"key\":\"" + token + "\"}";
        log.info(authMessage);
        WebSocketMessage message = new WebSocketMessage(authMessage, Constants.WS_OP_USER_AUTHENTICATION);
        this.send(message.toByteArrayMessage());
    }

    @Override
    public void onMessage(String message) {
        log.debug("onMessage");
    }

    @Override
    public void onMessage(ByteBuffer bytes) {
        log.debug("onMessage byte");
        WebSocketMessage message = new WebSocketMessage(bytes);
        switch (message.getOperation()) {
            case Constants.WS_OP_CONNECT_SUCCESS:
                log.debug(message.getBody());
                connected();
                break;
            case Constants.WS_OP_HEARTBEAT_REPLY:
                log.debug("heartbeat reply");
                break;
            case Constants.WS_OP_MESSAGE:
                log.debug(message.getBody());
                handleMesaage(message.getBody());
                break;
            default:
                log.error("UNSUPPORTED OPERATION:" + message.getOperation());
        }
    }

    @Override
    public void onClose(int code, String reason, boolean remote) {
        running = false;
        log.info("closed: code:" + code + " reason:" + reason + " remote:" + remote);
    }

    @Override
    public void onError(Exception ex) {
        log.error("onError", ex);
    }

    private void connected() {
        this.running = true;
        heartbeat.start();
    }

    private void handleMesaage(String message) {
        JSONObject msg = JSONUtil.parseObj(message);
        String cmd = msg.getStr("cmd");
        if (Constants.CMD_DANMU_MSG.equals(cmd)) {
            if (messageHandler != null) {
                JSONArray info = msg.getJSONArray("info");
                String text = info.getStr(1);
                int senderId = info.getJSONArray(2).getInt(0);
                String senderName = info.getJSONArray(2).getStr(1);

                try {
                    messageHandler.handleMessage(text, senderName, senderId);
                } catch (Exception e) {
                    log.error("message handle error.", e);
                }
            }
        }
    }
}
